package org.smile.db.pool;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import javax.sql.DataSource;

import org.apache.log4j.Logger;
/**
 * 一个基本的数据源实现类
 * @author strive
 */
public class BasicDataSource implements DataSource {
	/**
	 * 日志
	 */
	protected static final Logger logger=Logger.getLogger(BasicDataSource.class);
	/**
	 *最后一次连接数据源的时间
	 */
	private volatile long lastConnectTime;
	/**
	 * 标记记线程是否要运行
	 */
	private boolean isRun=true;
	
	/**
	 * 数据源参数
	 */
	private DataSourceParam param;
	/**
	 * 数据库连接清理线程
	 */
	private ConnectionClear clearor;
	
	/**
	 * 连接集合数据源中的所有连接
	 */
	List<PoolConnection> conns=new Vector<PoolConnection>();
	/**
	 * 构造一个数据数据源
	 *  在不使用此数据源，即把此数据源从连接池中删除必须关闭此数据源
	 * @param param
	 * @throws SQLException 
	 */
	BasicDataSource(DataSourceParam param) throws SQLException{
		this.param=param;
		conns.add(newPoolConnection(param,false));
		clearor=new ConnectionClear();
		clearor.start();
	}
	/**
	 * 得到连接数
	 * @return
	 */
	public synchronized int getConnectionCount(){
		return this.conns.size();
	}
	/**
	 * 得到空闲连接个数
	 * @return
	 */
	public synchronized int getFreeConnectionCount(){
		int count=0;
		for(int i=0;i<this.conns.size();i++){
			PoolConnection c=conns.get(i);
			if(!c.isUsed()){
				count++;
			}
		}
		return count;
	}
	/**
	 * 创建一个新的连接
	 * @param param
	 * @return
	 * @throws SQLException 
	 */
	private PoolConnection newPoolConnection(DataSourceParam param,boolean isUsed) throws SQLException{
		Connection conn;
		try {
			this.getClass().getClassLoader().loadClass(param.getDriver()).newInstance();
			conn= DriverManager.getConnection(param.getUrl(),param.getUser(), param.getPassword()); 
		} catch (Exception e) {
			throw new SQLException("连接数据库失败："+param.getUrl()+e);
		}
        // 代理将要返回的连接对象
       return new PoolConnection(conn,isUsed); 
	}
	/**
	 * 获取数据库连接
	 */
	public Connection getConnection(String user, String password) throws SQLException 
    { 
        // 首先从连接池中找出空闲的对象
        Connection conn = getFreeConnection(0); 
        if(conn == null){ 
            // 判断是否超过最大连接数 , 如果超过最大连接数
            // 则等待一定时间查看是否有空闲连接 , 否则抛出异常告诉用户无可用连接
            if(getConnectionCount() >= param.getMaxConnection()) 
            {
            	//达到最大时候，等待空闲连接
                conn = getFreeConnection(param.getWaitTime()); 
            }
            else{
            	// 没有超过连接数，重新获取一个数据库的连接
            	if(user!=null&&password!=null){
	            	param.setUser(user); 
	            	param.setPassword(password); 
            	}
                // 代理将要返回的连接对象
                PoolConnection _conn =newPoolConnection(param,true); 
                synchronized(conns){ 
                    conns.add(_conn); 
                } 
                conn = _conn.getConnection(); 
            } 
        }
        //最后一次连接时间设置为当前时间
        this.lastConnectTime=System.currentTimeMillis();
        return conn; 
    } 
	
    /** 
    * 从连接池中取一个空闲的连接
    * @param time     如果该参数值为 0 则没有连接时只是返回一个 null
    * 否则的话等待 nTimeout 毫秒看是否还有空闲连接，如果没有抛出异常
    * @return Connection 
    * @throws SQLException 
    */ 
    protected synchronized Connection getFreeConnection(long time) throws SQLException 
    { 
    	//设置尝试连接的间隔
    	final int TRY_TIME=5000;
        Connection conn = null; 
        //查询空闲连接
        Iterator iter = conns.iterator(); 
        while(iter.hasNext()){ 
            PoolConnection _conn = (PoolConnection)iter.next(); 
            if(!_conn.isUsed()){ 
                conn = _conn.getConnection(); 
                _conn.setInUsed(true);                 
                break; 
            } 
        }
        //没有空闲时，等待一个间隔就尝试一次，尝试的总时间为time
        while(conn == null && time > 0){ 
            // 等待 nTimeout 毫秒以便看是否有空闲连接
            try{ 
                Thread.sleep(TRY_TIME); 
            }catch(Exception e){} 
            conn = getFreeConnection(0); 
            //取一次 ，取的次数中等待的时间长决定 取的次数为：等待时间/5000
            time-=TRY_TIME;
        } 
        if(conn == null){
        	if(this.getConnectionCount()>=param.getMaxConnection()){
        		throw new SQLException("没有可用的数据库连接:wait time out ……………………"); 
        	}
        }
        return conn; 
    }
    
	public PrintWriter getLogWriter() throws SQLException {
		throw new SQLException("此方法还没有实现");
	}

	public int getLoginTimeout() throws SQLException {
		return (int) (System.currentTimeMillis()-this.lastConnectTime);
	}

	@Override
	public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException
	{
		return null;
	}

	public void setLogWriter(PrintWriter printwriter) throws SQLException {
		throw new SQLException("此方法还没有实现");
	}

	public void setLoginTimeout(int i) throws SQLException {
		this.lastConnectTime=System.currentTimeMillis()-i;
	}
	/**
	 * 获得数据库连接
	 */
	public Connection getConnection() throws SQLException {
		
		return this.getConnection(null, null);
	}
	/**
	 * 关闭数据源 会关闭源中的所有连接
	 */
	public synchronized void close() {
		
		for(Iterator itor=conns.iterator();itor.hasNext();){
			PoolConnection conn=(PoolConnection)itor.next();
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		conns.clear();
		this.isRun=false;
		clearor.interrupt();
	}
	/**
	 * 连接清理线程
	 * @author strive
	 *
	 */
	private class ConnectionClear extends Thread{
		@Override
		public void run() {
			while(isRun){
				try {
					//10分钟清理一次
					this.sleep(600000);
				} catch (InterruptedException e) {
					logger.warn("连接清理线程sleep中被中断,可能是调用了数据源关闭方法了,如不是则可能出现了未知的异常:[url:"+param.getUrl()+",user:"+param.getUser()+"]");
				}
				for(int i=0;i<conns.size();i++){
					PoolConnection con=conns.get(i);
					long currentTime=System.currentTimeMillis();
					//如果超时关闭
					if(currentTime-con.getLastAccessTime()>param.getTimeoutValue()){
						//保留设置的最少连接数
						if(conns.size()>param.getMinConnection()&&con.isUsed()==false){
							try {
								con.close();
								conns.remove(i);
							} catch (SQLException e) {
								logger.error("清理空闲的连接出现错误",e);
							}
						}
					}
				}
			}
			logger.info("连接池[url:"+param.getUrl()+",user:"+param.getUser()+"]清理线程被停止,可能是连接池已关闭");
		}
	}
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		// TODO Auto-generated method stub
		return false;
	}
	public <T> T unwrap(Class<T> iface) throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}
}
